package growth.net.peer;

import growth.net.utils.Utils;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

public class RequestProcessor {

    InetAddress senderIp = null;
    int senderPort = -1;
    String senderName = null;

    public void addNeighbour(String name, int listPort, int sendPort,
            InetAddress ip, ArrayList<Node> neighboursList) {
        Node node = new Node();
        node.name = name;
        node.ip = ip;
        node.listPort = listPort;
        node.sendPort = sendPort;
        neighboursList.add(node);
        SendingService.getSendingService().send(
                MessageTypes.ACKNOWLEDGE + "ADDED AS NEIGHBOUR", ip, listPort);
    }

    void sendBroadcastMessage(Message message) {
        String messagePacket = MessageTypes.MESSAGE + "," + message.sourceid + "," + message.id + ","
                + message.body + "," + message.timeToLive + "," + ListeningService.portNum + "," + ListeningService.ip
                + "," + Peer.peerName;
        for (Node node : Peer.neighboursList) {
            if (senderName != null) {
                if (node.name.equals(senderName) == false) {
//                    System.out.println("Fwd: " + node.name);
                    SendingService.getSendingService().send(messagePacket, node.ip,
                            node.listPort);
                }
            } else {
//                System.out.println("Fwd: " + node.name);jj
                SendingService.getSendingService().send(messagePacket, node.ip,
                        node.listPort);
            }
        }
    }

    void sendBroadcastInfoMessage(String requesterName, int requesterPort, InetAddress requesterIp, Message msg) {
        String messagePacket = MessageTypes.INFO_REQ + "," + requesterName + "," + requesterPort + "," + requesterIp + "," + msg.id;
        for (Node node : Peer.neighboursList) {
            if (node.name.equals(requesterName) == false) {
//                System.out.println("Fwd: " + node.name);
                SendingService.getSendingService().send(messagePacket, node.ip,
                        node.listPort);
            }
        }
    }

    public void processMessage(String inMsg) {
        String[] msgParams = Utils.messageParser(inMsg);
        int msgType = Integer.valueOf(msgParams[0].trim().toString());
        switch (msgType) {
            case MessageTypes.ACKNOWLEDGE:
                System.out.println("Ack received");
                break;
            case MessageTypes.REGISTERME:
//                System.out.println("Request to register");
                addNewNeighbor(msgParams);
                break;
            case MessageTypes.ADDMEREQUEST:
                try {
                    for (int i = 0; i < Peer.neighboursList.size(); i++) {
                        if (msgParams[1].toString().equals(
                                Peer.neighboursList.get(i).name)) {
                            return;
                        }
                    }
                    Node node = new Node();
                    node.name = msgParams[1];
                    node.listPort = Integer.parseInt(msgParams[2].trim());
                    node.sendPort = Integer.parseInt(msgParams[3].trim());
                    String[] splitAdd = msgParams[4].trim().split("/");
                    node.ip = InetAddress.getByName(splitAdd[1]);
                    Peer.neighboursList.add(node);
                    SendingService.getSendingService().send(
                            MessageTypes.ACKNOWLEDGE + ", ADDED BY " + Peer.peerName,
                            node.ip, node.listPort);
                } catch (UnknownHostException ex) {
                    Logger.getLogger(RequestProcessor.class.getName()).log(Level.SEVERE, null, ex);
                }
                break;
            case MessageTypes.MESSAGE:
                try {
//                    System.out.println("RX MSG PCKT: " + inMsg);
                    Message message = Message.createMessage(msgParams);

                    if (Peer.messageList.isEmpty()) {
                        System.out.println("Boradcast Msg: " + message.body);
//                        System.out.println("First Msg, Saving and forwarding ");
                        Peer.messageList.add(message);
                        String msgAck = MessageTypes.ACKNOWLEDGE + "," + "Ack of msg from "
                                + Peer.peerName;
                        senderPort = Integer.valueOf(msgParams[5].toString());
                        String[] splitAdd = msgParams[6].trim().split("/");
                        senderIp = InetAddress.getByName(splitAdd[1]);
                        senderName = msgParams[7].toString();
                        SendingService.getSendingService().send(msgAck, senderIp, senderPort);
                        sendBroadcastMessage(message);
                    } else {
                        boolean isAlreadySeen = false;
                        for (Message msg : Peer.messageList) {
                            if (msg.id.equals(message.id)) {
                                isAlreadySeen = true;
                                break;
                            }
                        }
                        if (isAlreadySeen == false) {
//                            System.out.println("Saving and forwarding message to neighbours");
                            System.out.println("Boradcast Msg: " + message.body);
                            Peer.messageList.add(message);
                            String msgAck = MessageTypes.ACKNOWLEDGE + "," + "Ack of msg from "
                                    + Peer.peerName;
                            senderPort = Integer.valueOf(msgParams[5].toString());
                            String[] splitAdd = msgParams[6].trim().split("/");
                            senderIp = InetAddress.getByName(splitAdd[1]);
                            senderName = msgParams[7].toString();
                            SendingService.getSendingService().send(msgAck, senderIp, senderPort);
                            sendBroadcastMessage(message);
                        } else {
                            System.out.println("Duplicate Message");
                        }
                    }


                } catch (UnknownHostException ex) {
                    ex.printStackTrace();
                }
                break;
            case MessageTypes.INFO_REQ:
                try {
//                    System.out.println("INFO REQ RECEIVED: " + inMsg);
                    Message msg = new Message();
                    msg.id = msgParams[4].toString();
                    if (Peer.messageList.isEmpty() == false) {
                        boolean isAlreadySeen = false;
                        for (Message m : Peer.messageList) {
                            if (m.id.equals(msg.id)) {
                                isAlreadySeen = true;
                                break;
                            }
                        }
                        if (isAlreadySeen == true) {
//                            System.out.println("Already processed");
                            break;
                        }
                    }
                    Peer.messageList.add(msg);
                    String neighborInfo = MessageTypes.INFO_RPLY + "," + Peer.peerName;
                    for (Node node : Peer.neighboursList) {
                        neighborInfo += "," + node.name;
                    }
                    senderName = msgParams[1].toString();
                    senderPort = Integer.valueOf(msgParams[2].toString());
                    String[] splitAdd = msgParams[3].trim().split("/");
                    senderIp = InetAddress.getByName(splitAdd[1]);
                    SendingService.getSendingService().send(neighborInfo, senderIp, senderPort);
                    sendBroadcastInfoMessage(senderName, senderPort, senderIp, msg);
                } catch (UnknownHostException ex) {
                    ex.printStackTrace();
                }
                break;
            case MessageTypes.ANALYSIS_DEG:

                System.out.println("Min Degree of Graph: " + msgParams[1].toString() + " Node: " + msgParams[2]);
                System.out.println("Max Degree of Graph: " + msgParams[3].toString() + " Node: " + msgParams[4]);
                break;
            case MessageTypes.ANALYSIS_DIA:
                System.out.println("Graph Diameter     : " + Integer.valueOf(msgParams[1].toString()));
                break;
            case MessageTypes.DELETEME:
//                System.out.print("DELETE_ME from "
//                        + msgParams[1].toString());
                int indexToDelete = -1;
                for (Node nbr : Peer.neighboursList) {
                    if (nbr.name.toString().equals(msgParams[1].toString().trim())) {
                        indexToDelete = Peer.neighboursList.indexOf(nbr);
                    }
                }
                if (indexToDelete > -1) {
                    Peer.neighboursList.remove(indexToDelete);
//                    System.out.println("...Deleted!!");
                } else {
                    System.out.println("DELETE Failed: Node not found");
                }
                break;
            case MessageTypes.LEAVE_NET:
                if (msgParams[1].toString().equals(Peer.peerName)) {
//                    System.out.println("You don't like me anymore :(");
                    sendLinkAllDeleteMeMessage(Peer.REGISTER_PORT, Peer.REGISTER_IP);
                    sendDeleteMeFromGraph(Peer.REGISTER_PORT, Peer.REGISTER_IP);
                    System.out.println(" Leaving the network, Bye Bye !!");
                    Peer.listeningService.stopListening();
                } else {
                    System.out.println("Not my turn yet !!");
                }
                break;
            case MessageTypes.SPAWN_NODE:
                try {
                    if (msgParams[1].toString().equals(Peer.peerName)) {
                        String[] params = {Peer.SCRIPT, "" + ListeningService.portNum};
                        Runtime.getRuntime().exec(params);
                        break;
                    } else {
                        System.out.println("Not my turn yet !!");
                    }
                } catch (IOException ex) {
                    Logger.getLogger(RequestProcessor.class.getName()).log(Level.SEVERE, null, ex);
                }
            case MessageTypes.INFO_RPLY:
                System.out.println("\nNeighbors of: " + msgParams[1].trim().toString() + "\n");
                for (int i = 2; i < msgParams.length; i++) {
                    System.out.println("---> " + msgParams[i]);
                }
                break;
            default:
                System.out.println("Unknown message received");

        }
    }

    public void sendReplaceMeWith(Node repNbr, Node withNbr) {
        String reqMsg = MessageTypes.REPLACEMENT + "," + Peer.peerName + ","
                + repNbr.name + "," + repNbr.listPort + "," + repNbr.sendPort
                + "," + repNbr.ip;
        SendingService.getSendingService().send(reqMsg, withNbr.ip,
                withNbr.listPort);
    }

    public void sendAddMeRequest(String fromNodeName, int fromNodeListeningPort, int fromNodeSendPort,
            InetAddress fromNodeIP, int toNodeListeningPort, InetAddress toNodeIP) {
        String message = MessageTypes.ADDMEREQUEST + "," + fromNodeName + "," + fromNodeListeningPort
                + "," + fromNodeSendPort + "," + fromNodeIP;
        SendingService.getSendingService().send(message, toNodeIP, toNodeListeningPort);
    }

    public void sendMessage(String to, String msg) {
        for (Node nbr : Peer.neighboursList) {
            if (nbr.name.equals(to)) {
                String msgStr = MessageTypes.MESSAGE + "," + msg + " from "
                        + Peer.peerName;
                SendingService.getSendingService().send(msgStr, nbr.ip,
                        nbr.listPort);
            }
        }

    }

    public void sendDeleteMeForever(Node node) {
        String reqMsg = MessageTypes.DELETEME + "," + Peer.peerName;
        System.out.println("Sending DELETEME to " + node.name + "MSG: " + reqMsg);
        SendingService.getSendingService().send(reqMsg, node.ip,
                node.listPort);
    }

    public void sendAddToList(Node nodeInfo, Node rcvrNode) {
        String reqMsg = MessageTypes.ADDMEREQUEST + "," + nodeInfo.name + ","
                + nodeInfo.listPort + "," + nodeInfo.sendPort + ","
                + nodeInfo.ip;
        SendingService.getSendingService().send(reqMsg, rcvrNode.ip,
                rcvrNode.listPort);
    }

    public void sendLinkAllDeleteMeMessage(int REGISTER_PORT, InetAddress REGISTER_IP) {
        if (1 == Peer.neighboursList.size()) {
            this.sendDeleteMeForever(Peer.neighboursList.get(0));
            return;
        }
        for (int i = 0; i < Peer.neighboursList.size(); i++) {
            for (int j = 0; j < Peer.neighboursList.size(); j++) {
                if (i != j) {
                    this.sendAddToList(Peer.neighboursList.get(j),
                            Peer.neighboursList.get(i));
                    this.sendAddEdgeToGraph(REGISTER_PORT, REGISTER_IP,
                            Peer.neighboursList.get(j).name, Peer.neighboursList.get(i).name);
                }
            }
        }
        for (int i = 0; i < Peer.neighboursList.size(); i++) {
            this.sendDeleteMeForever(Peer.neighboursList.get(i));
        }
    }

    public void sendCircularLinkDeleteMeMessage(int REGISTER_PORT, InetAddress REGISTER_IP) {
        if (1 == Peer.neighboursList.size()) {
            this.sendDeleteMeForever(Peer.neighboursList.get(0));
            return;
        }
        for (int i = 0; i < Peer.neighboursList.size(); i++) {
//            for (int j = 0; j < Peer.neighboursList.size(); j++) {
//                if (i != j) {
            if (i < Peer.neighboursList.size() - 1) {
                this.sendAddToList(Peer.neighboursList.get(i),
                        Peer.neighboursList.get(i + 1));
                this.sendAddToList(Peer.neighboursList.get(i + 1),
                        Peer.neighboursList.get(i));
                this.sendAddEdgeToGraph(REGISTER_PORT, REGISTER_IP,
                        Peer.neighboursList.get(i).name, Peer.neighboursList.get(i + 1).name);
            } else {
                this.sendAddToList(Peer.neighboursList.get(i),
                        Peer.neighboursList.get(0));
                this.sendAddToList(Peer.neighboursList.get(0),
                        Peer.neighboursList.get(i));
                this.sendAddEdgeToGraph(REGISTER_PORT, REGISTER_IP,
                        Peer.neighboursList.get(i).name, Peer.neighboursList.get(0).name);
            }

        }

        for (int i = 0; i < Peer.neighboursList.size(); i++) {
            this.sendDeleteMeForever(Peer.neighboursList.get(i));
        }
    }

    public void registerToGrowthNet(String name, int listPort, int sendPort,
            InetAddress ip, int rxPort, InetAddress rxAddress) {
        String message = MessageTypes.REGISTERME + "," + name + "," + listPort
                + "," + sendPort + "," + ip;
        SendingService.getSendingService().send(message, rxAddress, rxPort);
    }

    public void registerNeighborToGrowthNet(String name, int listPort, int sendPort,
            InetAddress ip, String parentName, int rxPort, InetAddress rxAddress) {
        String message = MessageTypes.REGISTER_SPAWN + "," + name + "," + listPort
                + "," + sendPort + "," + ip + "," + parentName;
        SendingService.getSendingService().send(message, rxAddress, rxPort);
    }

    public void sendDeleteMeFromGraph(int REGISTER_PORT, InetAddress REGISTER_IP) {
        System.out.println("Sending DELETEME from Graph " + Peer.peerName);
        String reqMsg = MessageTypes.DELETEVERTEX + "," + Peer.peerName;
        SendingService.getSendingService().send(reqMsg, REGISTER_IP,
                REGISTER_PORT);
    }

    public void sendAddEdgeToGraph(int REGISTER_PORT, InetAddress REGISTER_IP, String vertex1, String vertex2) {
        System.out.println("Sending add edge to graph " + Peer.peerName);
        String reqMsg = MessageTypes.ADDEEDGE + "," + vertex1 + "," + vertex2;
        SendingService.getSendingService().send(reqMsg, REGISTER_IP,
                REGISTER_PORT);
    }

    public void sendAddVertexToGraph(int REGISTER_PORT, InetAddress REGISTER_IP, String vertexName) {
        System.out.println("Sending add vertex to graph " + vertexName);
        String reqMsg = MessageTypes.ADDVERTEX + "," + vertexName;
        SendingService.getSendingService().send(reqMsg, REGISTER_IP,
                REGISTER_PORT);
    }

    void sendAnalysisDegreeMessage(String peerName, InetAddress ip, int portNum) {
        String messagePacket = MessageTypes.ANALYSIS_DEG + "," + Peer.peerName + "," + ListeningService.ip + "," + ListeningService.portNum;
        SendingService.getSendingService().send(messagePacket, ip, portNum);

    }

    void sendAnalysisDiaMessage(String peerName, InetAddress ip, int portNum) {
        String messagePacket = MessageTypes.ANALYSIS_DIA + "," + Peer.peerName + "," + ListeningService.ip + "," + ListeningService.portNum;
        SendingService.getSendingService().send(messagePacket, ip, portNum);

    }

    private void addNewNeighbor(String[] msgParams) {
        try {
            Node node = new Node();
            node.name = msgParams[1];
            node.listPort = Integer.parseInt(msgParams[2].trim());
            node.sendPort = Integer.parseInt(msgParams[3].trim());
            String[] splitAdd = msgParams[4].trim().split("/");
            node.ip = InetAddress.getByName(splitAdd[1]);
            Peer.neighboursList.add(node);
            sendAddMeRequest(Peer.peerName, ListeningService.portNum, SendingService.sendingPort, Peer.ip, node.listPort, node.ip);
            registerNeighborToGrowthNet(node.name, node.listPort,
                    node.sendPort, InetAddress.getLocalHost(), Peer.peerName,
                    Peer.REGISTER_PORT, InetAddress.getLocalHost());
        } catch (UnknownHostException ex) {
            ex.printStackTrace();
        }
    }

    public void sendMessageToLeave(Node node) {
        String reqMsg = MessageTypes.LEAVE_NET + "," + node.name;
        SendingService.getSendingService().send(reqMsg, node.ip,
                node.listPort);
    }

    public void sendMessageToSpawn(Node node) {
        String reqMsg = MessageTypes.SPAWN_NODE + "," + node.name;
        SendingService.getSendingService().send(reqMsg, node.ip,
                node.listPort);
    }
}
